﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace RoketPack.IM
{
    internal partial class FriendList : Form
    {
        private IM.Session m_Session = null;
        private RoketPack.Manager m_Manager = null;
        private bool p_NotConfigured = false;

        internal FriendList(IM.Session session, RoketPack.Manager manager)
        {
            InitializeComponent();
            this.m_Session = session;
            this.m_Manager = manager;

            if (this.m_Session != null)
            {
                this.m_Session.StatusUpdated += new ContactEventHandler(m_Session_StatusUpdated);
                this.m_Session.ContactAdded += new ContactEventHandler(m_Session_ContactAdded);
                this.m_Session.ContactChanged += new ContactEventHandler(m_Session_ContactChanged);
                this.m_Session.ContactRemoved += new ContactEventHandler(m_Session_ContactRemoved);
                this.m_Session.ConnectStart += new EventHandler(m_Session_ConnectStart);
                this.m_Session.ConnectFailure += new EventHandler(m_Session_ConnectFailure);
            }
            this.c_FriendUI.ContactChatStarted += new ContactEventHandler(c_FriendUI_ContactChatStarted);
            this.c_FriendUI.ContactNicknameChanged += new ContactEventHandler(c_FriendUI_ContactNicknameChanged);

            if (Aero.Enabled)
                this.Icon = Properties.Resources.RoketPackFormIconDark;
            else
                this.Icon = Properties.Resources.RoketPackFormIconLight;
        }

        /// <summary>
        /// This event is raised when a contact's status has been updated.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The contact event information.</param>
        void m_Session_StatusUpdated(object sender, ContactEventArgs e)
        {
            if (e.Contact.User == this.m_Session.Username && e.Contact.Server == this.m_Session.Server)
            {
                // The current user; update the UserStatus control.
                this.c_UserStatus.Username = e.Contact.Representation;
                this.c_UserStatus.Status = this.m_Session.Status;
                this.c_UserStatus.State = this.m_Session.State;
                this.c_StatusToolStripTextBox.Text = this.m_Session.Status;
                this.UpdateStateMenuItems(this.m_Session.State);

                RoketPack.Manager.VoidLambda lambda = () =>
                    {
                        this.Text = "Friend List (" + e.Contact.Representation + ")";
                    };
                if (this.InvokeRequired)
                    this.Invoke(lambda);
                else
                    lambda();
            }
            else
            {
                // One of our contacts.  We must check to make sure that they
                // are in the list (or not in the list) depending on their
                // online / offline status.
                ContactTreeNode ctn = null;
                foreach (object node in this.c_FriendUI.Items)
                {
                    if (node is ContactTreeNode)
                    {
                        ContactTreeNode ctnt = node as ContactTreeNode;
                        if (ctnt.Contact == e.Contact)
                        {
                            ctn = ctnt;
                            break;
                        }
                    }
                }

                if (ctn == null && e.Contact.Online)
                {
                    // The contact entry in the list wasn't found, but the
                    // user is now online, so we must add it to the list by
                    // emulating a ContactAdded event.
                    this.m_Session_ContactAdded(this, e);
                }
                else if (ctn != null && !e.Contact.Online)
                {
                    // The contact entry exists, but the contact is now
                    // offline.  Remove it from the list by emulating
                    // a ContactRemoved event.
                    this.m_Session_ContactRemoved(this, e);
                }
            }
        }

        /// <summary>
        /// This event is raised when a contact needs to be added to the
        /// FriendUI list.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The contact event information.</param>
        void m_Session_ContactAdded(object sender, ContactEventArgs e)
        {
            RoketPack.Manager.VoidLambda lambda = () =>
                {
                    // Check to make sure the contact has at least one group.
                    if (e.Contact.Groups.Length == 0)
                        e.Contact.AddGroup("Friends");

                    // Add the new contact tree node to all of the groups.
                    foreach (jabber.protocol.iq.Group group in e.Contact.Groups)
                    {
                        GroupTreeNode mnode = null;
                        foreach (object node in this.c_FriendUI.Items)
                        {
                            if (node is GroupTreeNode)
                            {
                                if (((GroupTreeNode)node).Name == group.GroupName)
                                {
                                    mnode = (GroupTreeNode)node;
                                    break;
                                }
                            }
                        }

                        if (mnode == null)
                        {
                            // Create the new GroupTreeNode.
                            mnode = new GroupTreeNode(group.GroupName);
                            mnode.AssociateList(this.c_FriendUI);
                            this.c_FriendUI.Items.Add(mnode);
                        }

                        ContactTreeNode cnode = new ContactTreeNode(e.Contact);
                        e.Contact.AssociateListItem(this.c_FriendUI, cnode);
                        if (e.Contact.Online)
                            this.c_FriendUI.Items.Insert(this.c_FriendUI.Items.IndexOf(mnode) + 1, cnode);
                        mnode.Children.Add(cnode);
                    }
                };

            if (this.c_FriendUI.InvokeRequired)
                this.c_FriendUI.Invoke(lambda);
            else
                lambda();
        }

        /// <summary>
        /// This event is raised when a contact needs to be updated in
        /// the FriendUI list.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The contact event information.</param>
        void m_Session_ContactChanged(object sender, ContactEventArgs e)
        {
        }

        /// <summary>
        /// This event is raised when a contact needs to be removed from
        /// the FriendUI list.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The contact event information.</param>
        void m_Session_ContactRemoved(object sender, ContactEventArgs e)
        {
            ContactTreeNode ctn = null;
            foreach (object node in this.c_FriendUI.Items)
            {
                if (node is ContactTreeNode)
                {
                    ContactTreeNode ctnt = node as ContactTreeNode;
                    if (ctnt.Contact == e.Contact)
                    {
                        ctn = ctnt;
                        break;
                    }
                }
            }

            this.c_FriendUI.Items.Remove(ctn);
        }

        /// <summary>
        /// This event is raised when the session starts connecting.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        void m_Session_ConnectStart(object sender, EventArgs e)
        {
            this.c_UserStatus.Status = "Connecting...";
        }

        /// <summary>
        /// This event is raised when the session fails to connect.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        void m_Session_ConnectFailure(object sender, EventArgs e)
        {
            this.c_UserStatus.Status = "Connection Failure";
        }

        /// <summary>
        /// This event is raised when a new chat is started with a contact,
        /// either initiated by the user or the contact.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The contact event information.</param>
        void c_FriendUI_ContactChatStarted(object sender, ContactEventArgs e)
        {
            if (this.m_Session != null)
                this.m_Session.Queue.Initiate(e.Contact);
        }

        /// <summary>
        /// This event is raised when the user has just finished changing the
        /// nickname for one of their contacts.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void c_FriendUI_ContactNicknameChanged(object sender, ContactEventArgs e)
        {
            this.m_Session.RenameContact(e.Contact, e.Nickname);
        }

        /// <summary>
        /// This event is raised when the form loads.  It is used to restore
        /// the position and size of the form.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void FriendList_Load(object sender, EventArgs e)
        {
            Point p = Properties.Settings.Default.FriendsListLocation;
            Size s = Properties.Settings.Default.FriendsListSize;
            if (!p.IsEmpty)
                this.Location = p;
            if (!s.IsEmpty)
                this.Size = s;
            this.WindowState = Properties.Settings.Default.FriendsListState;

            // Check to see if we are not configured for IM.
            if (this.p_NotConfigured)
            {
                this.c_UserStatus.Username = "Not Configured";
                this.c_UserStatus.Status = "Open the RoketPack settings dialog to configure IM.";
            }
        }

        /// <summary>
        /// This event is raised when the form has been asked to close.  It is
        /// used to save the position and size of the form.  In addition, it may
        /// cancel the close request and instead hide the form if it was initiated
        /// by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void FriendList_FormClosing(object sender, FormClosingEventArgs e)
        {
            Properties.Settings.Default.FriendsListState = this.WindowState;

            if (this.WindowState == FormWindowState.Normal)
            {
                Properties.Settings.Default.FriendsListSize = this.Size;
                Properties.Settings.Default.FriendsListLocation = this.Location;
            }
            else
            {
                Properties.Settings.Default.FriendsListSize = this.RestoreBounds.Size;
                Properties.Settings.Default.FriendsListLocation = this.RestoreBounds.Location;
            }

            Properties.Settings.Default.Save();

            // Now we actually cancel the close and instead hide the form.
            if (e.CloseReason == CloseReason.UserClosing)
            {
                this.Hide();
                e.Cancel = true;
            }
        }

        /// <summary>
        /// Indicates whether IM is currently not configured.
        /// </summary>
        internal bool NotConfigured
        {
            get
            {
                return this.p_NotConfigured;
            }
            set
            {
                this.p_NotConfigured = value;
            }
        }

        /// <summary>
        /// This event is fired when the context menu strip is opening after an item
        /// has been right-clicked on in the FriendUI.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information (with the possibility to cancel the event).</param>
        private void c_ListContextMenuStrip_Opening(object sender, CancelEventArgs e)
        {
            // See if the selected item is a contract tree node.
            ContactTreeNode ctn = this.c_FriendUI.SelectedItem as ContactTreeNode;
            if (ctn != null)
            {
                // Set the visibility.
                this.c_StartConversationToolStripMenuItem.Visible = true;
                this.c_EditNicknameToolStripMenuItem.Visible = true;
                this.c_EditGroupsToolStripMenuItem.Visible = true;
                this.c_MenuSeperator1.Visible = true;
                this.c_RemoveContactToolStripMenuItem.Visible = true;
                this.c_AddContactToolStripMenuItem.Visible = false;

                // Disable or enable the menu items.
                this.c_StartConversationToolStripMenuItem.Enabled = true;
                this.c_EditNicknameToolStripMenuItem.Enabled = true;
                this.c_EditGroupsToolStripMenuItem.Enabled = false;
                this.c_RemoveContactToolStripMenuItem.Enabled = true;
                this.c_AddContactToolStripMenuItem.Enabled = false;
                return;
            }
            else
            {
                // Set the visibility.
                this.c_StartConversationToolStripMenuItem.Visible = false;
                this.c_EditNicknameToolStripMenuItem.Visible = false;
                this.c_EditGroupsToolStripMenuItem.Visible = false;
                this.c_MenuSeperator1.Visible = false;
                this.c_RemoveContactToolStripMenuItem.Visible = false;
                this.c_AddContactToolStripMenuItem.Visible = true;

                // Disable or enable the menu items.
                this.c_StartConversationToolStripMenuItem.Enabled = false;
                this.c_EditNicknameToolStripMenuItem.Enabled = false;
                this.c_EditGroupsToolStripMenuItem.Enabled = false;
                this.c_RemoveContactToolStripMenuItem.Enabled = false;
                this.c_AddContactToolStripMenuItem.Enabled = true;
                return;
            }
        }

        /// <summary>
        /// This event is fired when the user presses a mouse button while
        /// over the form.  It is used to select the underlying item on
        /// right-click.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The mouse event information.</param>
        private void c_FriendUI_MouseDown(object sender, MouseEventArgs e)
        {
            // Get the item that was underneath the right mouse click.
            if (e.Button == MouseButtons.Right)
            {
                object node = null;
                for (int i = 0; i < this.c_FriendUI.Items.Count; i += 1)
                {
                    if (this.c_FriendUI.GetItemRectangle(i).Contains(e.Location))
                    {
                        node = this.c_FriendUI.Items[i];
                    }
                }

                this.c_FriendUI.SelectedItem = node;
            }
        }

        /// <summary>
        /// This event is fired when the Start Conversation context menu item
        /// is clicked by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_StartConversationToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Check to make sure an item is selected.
            if (this.c_FriendUI.SelectedItem == null)
                return;

            // Check to make sure the selected item is a contact.
            ContactTreeNode ctn = this.c_FriendUI.SelectedItem as ContactTreeNode;
            if (ctn != null)
            {
                // Start the conversation.
                if (this.m_Session != null)
                    this.m_Session.Queue.Initiate(ctn.Contact);
            }
        }

        /// <summary>
        /// This event is fired when the Add Contact context menu item
        /// or main menu item is clicked by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_AddContactToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AddContactForm f = new AddContactForm();
            if (f.ShowDialog() == DialogResult.Yes)
                this.m_Session.AddContact(f.ContactUsername, f.ContactNickname);
        }

        /// <summary>
        /// This event is fired when the Remove Contact context menu item
        /// or main menu item is clicked by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_RemoveContactToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Check to make sure an item is selected.
            if (this.c_FriendUI.SelectedItem == null)
                return;

            // Check to make sure the selected item is a contact.
            ContactTreeNode ctn = this.c_FriendUI.SelectedItem as ContactTreeNode;
            if (ctn != null)
            {
                // Start the conversation.
                if (this.m_Session != null)
                {
                    this.m_Session.RemoveContact(ctn.Contact);
                    this.m_Session_ContactRemoved(this, new ContactEventArgs(ctn.Contact, ContactEventArgs.ActionType.REMOVED));
                }
            }
        }

        /// <summary>
        /// This event is fired when the Edit Nickname context menu item
        /// or main menu item is clicked by the user.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_EditNicknameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // Check to make sure an item is selected.
            if (this.c_FriendUI.SelectedItem == null)
                return;

            // Check to make sure the selected item is a contact.
            ContactTreeNode ctn = this.c_FriendUI.SelectedItem as ContactTreeNode;
            if (ctn != null)
            {
                // Start editing the nickname if possible.
                this.c_FriendUI.BeginEdit(ctn);
            }
        }

        /// <summary>
        /// This event is fired when the Contact main menu drop down
        /// is opening.
        /// </summary>
        /// <param name="sender">The sender of the event.</param>
        /// <param name="e">The event information.</param>
        private void c_ContactsToolStripMenu_DropDownOpening(object sender, EventArgs e)
        {
            // See if the selected item is a contract tree node.
            ContactTreeNode ctn = this.c_FriendUI.SelectedItem as ContactTreeNode;
            if (ctn != null)
            {
                // Disable or enable the menu items.
                this.c_MainAddContactToolStripMenuItem.Enabled = true;
                this.c_MainEditNicknameToolStripMenuItem.Enabled = true;
                this.c_MainEditGroupsToolStripMenuItem.Enabled = false;
                this.c_MainRemoveContactToolStripMenuItem.Enabled = true;
                return;
            }
            else
            {
                // Disable or enable the menu items.
                this.c_MainAddContactToolStripMenuItem.Enabled = true;
                this.c_MainEditNicknameToolStripMenuItem.Enabled = false;
                this.c_MainEditGroupsToolStripMenuItem.Enabled = false;
                this.c_MainRemoveContactToolStripMenuItem.Enabled = false;
                return;
            }
        }

        private void c_OnlineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SetUserState(Contact.StateEnum.ONLINE);
        }

        private void c_AwayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SetUserState(Contact.StateEnum.AWAY);
        }

        private void c_ExtendedAwayToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SetUserState(Contact.StateEnum.EXTENDEDAWAY);
        }

        private void c_BusyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SetUserState(Contact.StateEnum.BUSY);
        }

        private void c_InvisibleToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SetUserState(Contact.StateEnum.INVISIBLE);
        }

        private void c_OfflineToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.SetUserState(Contact.StateEnum.OFFLINE);
        }

        private void SetUserState(Contact.StateEnum state)
        {
            if (this.m_Session == null)
                return;

            this.m_Session.SetUserState(state);
            this.UpdateStateMenuItems(state);
        }

        private void UpdateStateMenuItems(Contact.StateEnum state)
        {
            this.c_OnlineToolStripMenuItem.Checked = false;
            this.c_AwayToolStripMenuItem.Checked = false;
            this.c_ExtendedAwayToolStripMenuItem.Checked = false;
            this.c_BusyToolStripMenuItem.Checked = false;
            this.c_InvisibleToolStripMenuItem.Checked = false;
            this.c_OfflineToolStripMenuItem.Checked = false;
            switch (state)
            {
                case Contact.StateEnum.ONLINE:
                    this.c_OnlineToolStripMenuItem.Checked = true;
                    break;
                case Contact.StateEnum.AWAY:
                    this.c_AwayToolStripMenuItem.Checked = true;
                    break;
                case Contact.StateEnum.EXTENDEDAWAY:
                    this.c_ExtendedAwayToolStripMenuItem.Checked = true;
                    break;
                case Contact.StateEnum.BUSY:
                    this.c_BusyToolStripMenuItem.Checked = true;
                    break;
                case Contact.StateEnum.INVISIBLE:
                    this.c_InvisibleToolStripMenuItem.Checked = true;
                    break;
                case Contact.StateEnum.OFFLINE:
                    this.c_OfflineToolStripMenuItem.Checked = true;
                    break;
            }
        }

        private void c_StatusToolStripTextBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (this.m_Session == null)
                return;

            if (e.KeyCode == Keys.Enter)
            {
                this.m_Session.Status = c_StatusToolStripTextBox.Text;
                this.SetUserState(this.m_Session.State);
                this.c_StatusToolStripMenu.HideDropDown();
            }
        }

        private void c_StatusToolStripMenu_DropDownOpening(object sender, EventArgs e)
        {
            this.c_OnlineToolStripMenuItem.Enabled = (this.m_Session != null);
            this.c_AwayToolStripMenuItem.Enabled = (this.m_Session != null);
            this.c_ExtendedAwayToolStripMenuItem.Enabled = (this.m_Session != null);
            this.c_BusyToolStripMenuItem.Enabled = (this.m_Session != null);
            this.c_InvisibleToolStripMenuItem.Enabled = (this.m_Session != null);
            this.c_OfflineToolStripMenuItem.Enabled = (this.m_Session != null);
            this.c_StatusToolStripTextBox.Enabled = (this.m_Session != null);

            if (this.m_Session == null)
            {
                this.c_OnlineToolStripMenuItem.Checked = false;
                this.c_AwayToolStripMenuItem.Checked = false;
                this.c_ExtendedAwayToolStripMenuItem.Checked = false;
                this.c_BusyToolStripMenuItem.Checked = false;
                this.c_InvisibleToolStripMenuItem.Checked = false;
                this.c_OfflineToolStripMenuItem.Checked = false;
            }
        }

        private void c_ConfigureIMToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.m_Manager.OpenSettings(this, e);
        }

        private void c_StatusToolStripMenu_DropDownClosed(object sender, EventArgs e)
        {
            if (this.m_Session == null)
                return;

            if (this.m_Session.Status != c_StatusToolStripTextBox.Text)
            {
                this.m_Session.Status = c_StatusToolStripTextBox.Text;
                this.SetUserState(this.m_Session.State);
                this.c_StatusToolStripTextBox.Modified = false;
            }
        }
    }
}
